package com.aaron.gesturehelper.commandserver;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.UnknownHostException;
import java.nio.channels.FileChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.aaron.gesturehelper.commandserver.ScreenCapture.ScreenData;

import android.app.ActivityThread;
import android.app.StatusBarManager;
import android.content.Context;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.os.Environment;
import android.os.IPowerManager;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.util.Log;
import android.view.IWindowManager;
import android.view.KeyEvent;

public class CommandHandleServer {
	private static final boolean DEBUG = false;
	
	public static final String TAG = "CommandHandleServer";
	private static final String SERVER_ADDRESS = "com.aaron.gesturehelper";
	private Thread mThread;
	private LocalServerSocket mServer;
	private ExecutorService mThreadPool;

	private IWindowManager mWM = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
	
	private IPowerManager mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
	
	private String mTempFile = Environment.getExternalStorageDirectory().getAbsolutePath()+"/GestureHelper/tmp.pic";
	
	public void start() {
		try {
			mServer = new LocalServerSocket(SERVER_ADDRESS);
			if(DEBUG) {
				Log.i(TAG,"create ServerSocket");
			}
		} catch (UnknownHostException e) {
			if(DEBUG) {
				e.printStackTrace();
			}
		} catch (IOException e) {
			if(DEBUG) {
				e.printStackTrace();
			}
		}
		
		mThread = new Thread() {
			public void run(){
				while(Thread.currentThread() == mThread) {
					try {
						LocalSocket client = mServer.accept();
						if(mThreadPool != null) {
							if(DEBUG) {
								Log.i(TAG,"mThreadPool != null");
							}
		                    mThreadPool.submit(new CommandHandler(client));
		                } else {
		                	if(DEBUG) {
		                		Log.i(TAG,"mThreadPool == null");
		                	}
		                    try {
		                        client.close();
		                    } catch (IOException e) {
		                    	if(DEBUG) {
		                    		e.printStackTrace();
		                    	}
		                    }
		                }
					} catch (IOException e) {
						if(DEBUG) {
							e.printStackTrace();
						}
					}
				}
			}
			
		};
		mThreadPool = Executors.newFixedThreadPool(2);
		
		mThread.start();
	}

	public void stop() {
		if (mThread != null) {

			mThread.interrupt();
			if (mThreadPool != null) {
				try {
					mThreadPool.shutdownNow();
				} catch (SecurityException e) {
					if(DEBUG) {
						e.printStackTrace();
					}
				}
			}
			mThreadPool = null;
			mThread = null;
			try {
				mServer.close();
			} catch (IOException e) {
				if(DEBUG) {
					e.printStackTrace();
				}
			}
			mServer = null;
			System.exit(0);
		}
	}

	private class CommandHandler implements Runnable {
		private LocalSocket mClient;
		private BufferedWriter mOut;
		public CommandHandler(LocalSocket s) {
			if(DEBUG) {
				Log.i(TAG,"New CommandHandler");
			}
			mClient = s;
			try {
				mOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
			} catch (IOException e) {
				if(DEBUG) {
					e.printStackTrace();
				}
			}
		}
		
		private void handleKeyEvent(String[] params) {
			long now = SystemClock.uptimeMillis();
			
			Integer keycode = Integer.parseInt(params[1]);
			
			KeyEvent down = new KeyEvent(now, now ,KeyEvent.ACTION_DOWN,keycode, 0);
			KeyEvent up = new KeyEvent(now , now, KeyEvent.ACTION_UP,keycode, 0);
			if(DEBUG) {
				Log.i(TAG,"handle keyevent:"+keycode);
			}
			boolean longpress = false;
			if(params.length == 3) {//flags
				int flags ;
				if(params[2].startsWith("0x")||params[2].startsWith("0X")) {
					flags = Integer.parseInt(params[2].replaceAll("^0[x|X]", ""), 16);
				} else {
					flags = Integer.parseInt(params[2]);
				}
				if(flags == KeyEvent.FLAG_LONG_PRESS) {
					longpress = true;
				}
				if(DEBUG) {
					Log.i(TAG,"handle keyevent:"+keycode+"  flag:"+flags);
				}
			}
			try {
				mWM.injectKeyEvent(down, true);
				if(longpress) {
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						if(DEBUG) {
							e.printStackTrace();
						}
					}
				}
				mWM.injectKeyEvent(up, true);
			} catch (RemoteException e) {
				if(DEBUG) {
					e.printStackTrace();
				}
			}	
		}
		
		private void handleCustomEvent(String[] params) {
			if(params[1].equals("capturescreen")||params[1].equals("magnifier")) {
				ScreenData data = null;
				FileChannel wChannel = null;
				try {
					data = ScreenCapture.captureScreen();
					File f = new File(mTempFile);
					if(f.exists()) {
						f.delete();
					} 
					f.createNewFile();
					
					wChannel = new FileOutputStream(f, false).getChannel();
					wChannel.write(data.buf);
					mOut.write(params[1]+" bpp:"+data.bpp+" width:"+data.width+" height:"+data.height+"\n");
					mOut.flush();
				} catch (Exception e) {
					if(DEBUG) {
						Log.i(TAG,e.getMessage());
					}
				} finally {
					if(wChannel != null) {
						try {
							wChannel.close();
						} catch (IOException e) {
							if(DEBUG) {
								e.printStackTrace();
							}
						}
						wChannel = null;
					}
					data = null;
				}	
			} else if(params[1].equals("screenoff")) {
				long now = SystemClock.uptimeMillis();
				try {
					mPower.goToSleep(now);
				} catch (RemoteException e) {
					if(DEBUG) {
						e.printStackTrace();
					}
				}
			} else if(params[1].equals("shutdown")) {
				ShutdownThread.shutdown(getContext());
			} else if(params[1].equals("reboot")) {
				ShutdownThread.reboot(getContext(), "");
			} else if(params[1].equals("expand_statusbar")) {
				StatusBarManager bar = (StatusBarManager) getContext().getSystemService(Context.STATUS_BAR_SERVICE);
				bar.expand();
			}
		}
		
		private Context getContext() {
			ActivityThread thread = ActivityThread.currentActivityThread();
			if(thread == null) {
				Looper.prepare();
				thread = ActivityThread.systemMain();
			}
			return thread.getSystemContext();
		}
		
		private void handleCommand(String cmd) {
			if(DEBUG) {
				Log.i(TAG,"handle cmd:"+cmd);
			}
			String[] params = cmd.split("-");
			if(params[0].equals("key")) {
				handleKeyEvent(params);
			} else if(params[0].equals("custom")) {
				handleCustomEvent(params);
			}
		}
		
		public void run() {
			if(DEBUG) {
				Log.i(TAG,"new client run!");
			}
            BufferedReader in = null;
            try {
                in = new BufferedReader(new InputStreamReader(mClient.getInputStream()));
                String cmd;
                while(!(cmd = in.readLine()).equals("exit")) {
                	handleCommand(cmd);
                	if(DEBUG) {
                		Log.i(TAG,cmd);
                	}
                }
                if(DEBUG) {
                	Log.i(TAG,cmd);
                }
            } catch(IOException e) {
            	if(DEBUG) {
            		e.printStackTrace();
            	}
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                    	if(DEBUG) {
                    		e.printStackTrace();
                    	}
                    }
                    in = null;
                }
                if (mClient != null) {
                    try {
                    	mOut.close();
                        mClient.close();
                    } catch (IOException e) {
                    	if(DEBUG) {
                    		e.printStackTrace();
                    	}
                    }
                    mOut = null;
                    mClient = null;
                }
                mWM = null;
                CommandHandleServer.this.stop();
            }
        }
	};
	
	public void waitToDie() {
		try {
			mThread.join();
		} catch (InterruptedException e) {
			if(DEBUG) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		if(DEBUG) {
			Log.i(TAG,"main");
		}
		CommandHandleServer server = new CommandHandleServer();
		server.start();
		server.waitToDie();
	}
}

